package rpc.client;

import com.alibaba.fastjson.JSON;
import lombok.SneakyThrows;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import rpc.annotation.ServiceName;
import rpc.common.NodeInformation;
import rpc.common.RpcConfig;
import rpc.common.TaskRpc;
import rpc.units.SocketUnits;
import 我的JDK基础数据结构.HashMap.HashMap;

import java.io.IOException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.Map;

public class ProxyInterceptor implements MethodInterceptor {

    //根据serverName获取节点信息
    private Map<String,NodeInformation> nodeInformationMap=new HashMap<>();

    //代理类本身+拦截原方法+入参+代理的方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        ServiceName annotation = method.getDeclaringClass().getAnnotation(ServiceName.class);
        if(annotation==null||annotation.name().equals("")){
            throw new RuntimeException("请指明服务方名称");
        }
        String serverName = annotation.name();

        TaskRpc taskRpc=new TaskRpc(serverName,method.getName(),objects);
        NodeInformation nodeInformation = nodeInformationMap.get(serverName);
        if(nodeInformation==null){
            //去注册中心获取信息
            String nodeStr=this.callService(taskRpc);
            if(nodeStr.equals("null")){
                throw new RuntimeException("服务未注册");
            }
            nodeInformation = JSON.parseObject(nodeStr, NodeInformation.class);
            nodeInformationMap.put(serverName,nodeInformation);
        }

        //去调用服务端
        String result = this.callServer(nodeInformation,JSON.toJSONString(taskRpc));
        Class<?> returnType = method.getReturnType();

        return JSON.parseObject(result,returnType);
    }

    private String callService(TaskRpc taskRpc) throws IOException {
        Socket socket = new Socket(RpcConfig.CenterAddress,RpcConfig.CenterPort);
        SocketUnits.sendMsg(socket, JSON.toJSONString(taskRpc));
        return SocketUnits.receiveMsg(socket);
    }

    @SneakyThrows
    private String callServer(NodeInformation nodeInformation,String msg) {
        Socket socket=new Socket();
        SocketAddress socketAddress=new InetSocketAddress(nodeInformation.getIp(),nodeInformation.getPort());
        socket.connect(socketAddress);
        SocketUnits.sendMsg(socket,msg);
        String result = SocketUnits.receiveMsg(socket);
        this.close(socket);
        return result;
    }

    private void close(Socket socket) throws IOException {
        socket.close();
    }

}
